package com.huawei.cloudapp.model.direct;

import static com.huawei.cloudapp.utils.CasConstantsUtil.PHONE_NAME;
import static com.huawei.cloudapp.utils.CasConstantsUtil.SERVER_ID;
import static com.huawei.cloudapp.utils.CasConstantsUtil.STATUS;
import static com.huawei.cloudapp.utils.CasConstantsUtil.X_AUTH_TOKEN;

import static java.net.HttpURLConnection.HTTP_BAD_REQUEST;
import static java.net.HttpURLConnection.HTTP_FORBIDDEN;
import static java.net.HttpURLConnection.HTTP_INTERNAL_ERROR;
import static java.net.HttpURLConnection.HTTP_NOT_FOUND;
import static java.net.HttpURLConnection.HTTP_OK;
import static java.net.HttpURLConnection.HTTP_UNAUTHORIZED;

import android.util.Log;

import androidx.annotation.NonNull;

import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
import com.huawei.cloudapp.model.IPhoneModel;
import com.huawei.cloudapp.model.OnRequestListener;
import com.huawei.cloudapp.model.bean.CustomException;
import com.huawei.cloudapp.model.bean.direct.PhoneJobRequest;
import com.huawei.cloudapp.model.bean.direct.CPHError;
import com.huawei.cloudapp.model.bean.direct.PhoneDetail;
import com.huawei.cloudapp.model.bean.direct.PhoneList;
import com.huawei.cloudapp.model.bean.Phone;
import com.huawei.cloudapp.model.bean.PhoneInfo;
import com.huawei.cloudapp.model.bean.User;
import com.huawei.cloudapp.model.bean.direct.PhoneJobResponse;
import com.huawei.cloudapp.utils.CasConstantsUtil;
import com.huawei.cloudapp.utils.CasHttpUtils;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;

import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.HttpUrl;
import okhttp3.Response;

public class PhoneModel implements IPhoneModel {
    private static final String TAG = "PhoneModel";
    private String mRegion;
    private int mTotalCount;
    private String mUrl;
    @Override
    public void getPhoneList(User user, HashMap<String, String> condition, String region, String projectId, int offset, int limit, OnRequestListener<PhoneInfo> onRequestListener) {
        String userTokenExpireTime = user.getUserTokenExpireTimeByRegion(region);
        if (userTokenExpireTime == null || userTokenExpireTime.isEmpty()
                || Long.parseLong(userTokenExpireTime) + 5000 < System.currentTimeMillis()) {
            onRequestListener.onFailure(new CustomException.TokenExpireException());
            return;
        }
        mRegion = region;
        HashMap<String, String> requestHeader = new HashMap<String, String>();
        requestHeader.put(X_AUTH_TOKEN, user.getUserTokenByRegion(region));
        CasHttpUtils.get(getRequestPhoneListUrl(condition, region, projectId, offset, limit), requestHeader, new Callback() {
            @Override
            public void onFailure(@NonNull Call call, @NonNull IOException e) {
                onRequestListener.onFailure(e);
            }

            @Override
            public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
                try {
                    onRequestListener.onSuccess(parseResponseToPhoneList(response), mTotalCount);
                } catch (Exception e) {
                    onRequestListener.onFailure(e);
                }
            }
        });
    }

    @Override
    public void getPhoneDetailInfo(User user, String region, String projectId, String phoneId, OnRequestListener<PhoneDetail> onRequestListener) {
        String userTokenExpireTime = user.getUserTokenExpireTimeByRegion(region);
        if (userTokenExpireTime == null || userTokenExpireTime.isEmpty()
                || Long.parseLong(userTokenExpireTime) + 5000 < System.currentTimeMillis()) {
            onRequestListener.onFailure(new CustomException.TokenExpireException());
            return;
        }
        mRegion = region;
        HashMap<String, String> requestHeader = new HashMap<String, String>();
        requestHeader.put(X_AUTH_TOKEN, user.getUserTokenByRegion(region));
        CasHttpUtils.get(getRequestPhoneDetailUrl(region, projectId, phoneId), requestHeader, new Callback() {
            @Override
            public void onFailure(@NonNull Call call, @NonNull IOException e) {
                onRequestListener.onFailure(e);
            }

            @Override
            public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
                try {
                    onRequestListener.onSuccess(parseResponseToPhoneDetail(response), 1);
                } catch (Exception e) {
                    onRequestListener.onFailure(e);
                }
            }
        });
    }

    @Override
    public void resetPhone(User user,
                           String region,
                           String projectId,
                           List<String> phoneIdList,
                           String imageId,
                           List<HashMap<String, String>> property,
                           OnRequestListener<PhoneJobResponse> onRequestListener) {
        doRestartOrResetPhone(false, user, region, projectId, phoneIdList, imageId, property, onRequestListener);
    }


    @Override
    public void restartPhone(User user,
                             String region,
                             String projectId,
                             List<String> phoneIdList,
                             String imageId,
                             List<HashMap<String, String>> property,
                             OnRequestListener<PhoneJobResponse> onRequestListener) {

        doRestartOrResetPhone(true, user, region, projectId, phoneIdList, imageId, property, onRequestListener);
    }


    private void doRestartOrResetPhone(boolean isRestart,
                                       User user,
                                       String region,
                                       String projectId,
                                       List<String> phoneIdList,
                                       String imageId,
                                       List<HashMap<String, String>> property,
                                       OnRequestListener<PhoneJobResponse> onRequestListener) {
        String userTokenExpireTime = user.getUserTokenExpireTimeByRegion(region);
        if (userTokenExpireTime == null || userTokenExpireTime.isEmpty()
                || Long.parseLong(userTokenExpireTime) + 5000 < System.currentTimeMillis()) {
            onRequestListener.onFailure(new CustomException.TokenExpireException());
            return;
        }
        mRegion = region;
        HashMap<String, String> requestHeader = new HashMap<String, String>();
        requestHeader.put(X_AUTH_TOKEN, user.getUserTokenByRegion(region));

        PhoneJobRequest phoneJob = new PhoneJobRequest();
        if (property == null) {
            for (int i = 0; i < phoneIdList.size(); i++) {
                phoneJob.getPhones().add(new PhoneJobRequest.PhoneProperty(phoneIdList.get(i), null));
            }
        } else {
            for (int i = 0; i < phoneIdList.size(); i++) {
                phoneJob.getPhones().add(new PhoneJobRequest.PhoneProperty(phoneIdList.get(i), property.get(i).toString()));
            }
        }
        Gson gson = new Gson();
        String bodyStr = gson.toJson(phoneJob);
        String url = isRestart ? getRequestPhoneRestartUrl(region, projectId) : getRequestPhoneResetUrl(region, projectId);
        CasHttpUtils.post(url, requestHeader, bodyStr, new Callback() {
            @Override
            public void onFailure(@NonNull Call call, @NonNull IOException e) {
                onRequestListener.onFailure(e);
            }

            @Override
            public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
                try {
                    List<PhoneJobResponse> jobs = parseResponseToPhoneRestartOrResetJob(response);
                    onRequestListener.onSuccess(jobs, jobs.size());
                } catch (Exception e) {
                    onRequestListener.onFailure(e);
                }
            }
        });
    }


    private List<PhoneInfo> parseResponseToPhoneList(Response response) throws Exception {
        if (response == null) {
            Log.e(TAG, "Failed to get response from server.");
            throw new CustomException.EmptyResponseFromServerException();
        }
        String rspJsonStr = response.body().string();
        Gson gson = new Gson();
        List<PhoneInfo> phoneInfoList = new ArrayList<>();
        if (response.code() == HTTP_OK) {
            PhoneList rsp;
            try {
                rsp = gson.fromJson(rspJsonStr, PhoneList.class);
            } catch (JsonSyntaxException e) {
                Log.e(TAG, "Failed to parse rsp json. " + rspJsonStr);
                throw new CustomException.FailedToGetPhoneListException();
            }
            if (rsp == null) {
                Log.e(TAG, "Failed to get rsp from json. " + response.code() + ", " + rspJsonStr);
                throw new CustomException.FailedToGetPhoneListException();
            }
            mTotalCount = rsp.getCount();
            for (Phone phone : rsp.getPhones()) {
                //获取phone model规格
                phoneInfoList.add(new PhoneInfo(phone, "", CasConstantsUtil.REGION_INFO.get(mRegion)));
            }
        } else {
            CPHError rsp;
            try {
                rsp = gson.fromJson(rspJsonStr, CPHError.class);
            } catch (JsonSyntaxException e) {
                Log.e(TAG, "Failed to parse rsp json. " + rspJsonStr);
                throw new CustomException.FailedToGetPhoneListException();
            }
            handleCommonErrorResponse(response, rsp);
            Log.e(TAG, "Failed to get phone list. " + rspJsonStr);
            throw new CustomException.FailedToGetPhoneListException();
        }
        return phoneInfoList;
    }

    private List<PhoneDetail> parseResponseToPhoneDetail(Response response) throws Exception  {
        if (response == null) {
            Log.e(TAG, "Failed to get response from server.");
            throw new CustomException.EmptyResponseFromServerException();
        }
        String rspJsonStr = response.body().string();
        Gson gson = new Gson();
        PhoneDetail phoneDetail = null;
        if (response.code() == HTTP_OK) {
            try {
                phoneDetail = gson.fromJson(rspJsonStr, PhoneDetail.class);
            } catch (JsonSyntaxException e) {
                Log.e(TAG, "Failed to parse rsp json. " + rspJsonStr);
                throw new CustomException.FailedToGetPhoneDetailException();
            }
            if (phoneDetail == null) {
                Log.e(TAG, "Failed to get rsp from json. " + response.code() + ", " + rspJsonStr);
                throw new CustomException.FailedToGetPhoneDetailException();
            }
        } else {
            CPHError rsp;
            try {
                rsp = gson.fromJson(rspJsonStr, CPHError.class);
            } catch (JsonSyntaxException e) {
                Log.e(TAG, "Failed to parse rsp json. " + rspJsonStr);
                throw new CustomException.FailedToGetPhoneDetailException();
            }
            handleCommonErrorResponse(response, rsp);
            Log.e(TAG, "Failed to get phone detail. " + rspJsonStr);
            throw new CustomException.FailedToGetPhoneDetailException();

        }
        return Collections.singletonList(phoneDetail);
    }

    private List<PhoneJobResponse> parseResponseToPhoneRestartOrResetJob(Response response) throws Exception {
        if (response == null) {
            Log.e(TAG, "Failed to get response from server.");
            throw new CustomException.EmptyResponseFromServerException();
        }
        String rspJsonStr = response.body().string();
        Gson gson = new Gson();
        PhoneJobResponse phoneJob = null;
        Log.e(TAG, "rsp: " + rspJsonStr);
        if (response.code() == HTTP_OK) {
            try {
                phoneJob = gson.fromJson(rspJsonStr, PhoneJobResponse.class);
            } catch (JsonSyntaxException e) {
                Log.e(TAG, "Failed to parse rsp json. " + rspJsonStr);
                throw new CustomException.DeliverJobFailedException();
            }
            if (phoneJob == null) {
                Log.e(TAG, "Failed to get rsp from json. " + response.code() + ", " + rspJsonStr);
                throw new CustomException.DeliverJobFailedException();
            }
        } else {
            CPHError rsp;
            try {
                rsp = gson.fromJson(rspJsonStr, CPHError.class);
            } catch (JsonSyntaxException e) {
                Log.e(TAG, "Failed to parse rsp json. " + rspJsonStr);
                throw new CustomException.DeliverJobFailedException();
            }
            handleCommonErrorResponse(response, rsp);
            Log.e(TAG, "Failed to delivering job. " + rspJsonStr);
            throw new CustomException.DeliverJobFailedException();

        }
        return Collections.singletonList(phoneJob);
    }

    private void handleCommonErrorResponse(Response response, CPHError rsp) throws IOException {
        if (response.code() == HTTP_BAD_REQUEST) {
            if (rsp.getErrorCode().equals("CPS.0025")) {
                Log.e(TAG, "Phone name is invalid, rsp: " + response.code() + ", " + rsp.getErrorCode() + ", " + rsp.getErrorMsg()+ ", " + rsp.getRequestId());
                throw new CustomException.ParamInvalidException();
            } else if (rsp.getErrorCode().equals("CPS.0086")) {
                Log.e(TAG, "Server id is invalid, rsp: " + response.code() + ", " + rsp.getErrorCode() + ", " + rsp.getErrorMsg()+ ", " + rsp.getRequestId());
                throw new CustomException.ParamInvalidException();
            }
        } else if (response.code() == HTTP_UNAUTHORIZED) {
            if (rsp.getErrorCode().equals("APIGW.0301") || rsp.getErrorCode().equals("APIGW.0307")) {
                Log.e(TAG, "Token is invalid, need get new token. ");
                throw new CustomException.TokenExpireException();
            }  else if (rsp.getErrorCode().equals("APIGW.0303") || rsp.getErrorCode().equals("APIGW.0305")) {
                Log.e(TAG, "Check whether the account has the permission to request APIs.");
                throw new CustomException.NoPermissionException();
            }
        } else if (response.code() == HTTP_FORBIDDEN) {
            switch (rsp.getErrorCode()) {
                case "APIGW.0302":
                case "APIGW.0304":
                case "APIGW.0306":
                case "CPS.0001":
                    Log.e(TAG, "Check whether the account has the permission to request APIs.");
                    throw new CustomException.NoPermissionException();
                case "CPS.0006":
                    Log.e(TAG, "Role is op_restricted. ");
                    throw new CustomException.OpRestrictedException();
                case "CPS.0007":
                    Log.e(TAG, "Role is op_suspended. ");
                    throw new CustomException.OpSuspendedException();
                case "CPS.0008":
                    Log.e(TAG, "Role is op_unverified.");
                    throw new CustomException.OpUnverifiedException();
            }
        } else if (response.code() == HTTP_NOT_FOUND) {
            switch (rsp.getErrorCode()) {
                case "CPS.0005":
                    Log.e(TAG, "Phone not found.");
                    throw new CustomException.PhoneNotFoundException();
                case "CPS.0011":
                    Log.e(TAG, "job not found. ");
                    throw new CustomException.JobNotFoundException();
                case "CPS.0015":
                    Log.e(TAG, "Server not found. ");
                    throw new CustomException.ServerNotFoundException();
                case "CPS.0243":
                    Log.e(TAG, "User not found.");
                    throw new CustomException.UserNotFoundException();
                case "CPS.0302":
                    Log.e(TAG, "User not found.");
                    throw new CustomException.ResourceNotFoundException();
            }
        } else if (response.code() == HTTP_INTERNAL_ERROR) {
            Log.e(TAG, "Service internal error.");
            throw new CustomException.ServerError();
        }
    }

    private String getRequestPhoneListUrl(HashMap<String, String> condition, String region, String projectId, int offset, int limit) {
        HttpUrl.Builder urlBuilder = HttpUrl.parse("https://cph." + region + ".myhuaweicloud.com/v1/"+ projectId + "/cloud-phone/phones").newBuilder();

        if (offset >= 0) {
            urlBuilder.addQueryParameter("offset", String.valueOf(offset));
        }

        if (limit > 0) {
            urlBuilder.addQueryParameter("limit", String.valueOf(limit));
        }

        if (condition != null) {
            if (condition.containsKey("server_id")) {
                urlBuilder.addQueryParameter("server_id", condition.get(SERVER_ID));
            }
            if (condition.containsKey("phone_name")) {
                urlBuilder.addQueryParameter("phone_name", condition.get(PHONE_NAME));
            }
            if (condition.containsKey("status")) {
                urlBuilder.addQueryParameter("status", condition.get(STATUS));
            }
        }
        mUrl = urlBuilder.build().toString();
        return mUrl;
    }
    private String getRequestPhoneDetailUrl(String region, String projectId, String phoneId) {
        HttpUrl.Builder urlBuilder = HttpUrl.parse("https://cph." + region + ".myhuaweicloud.com/v1/"+ projectId + "/cloud-phone/phones/" + phoneId).newBuilder();
        mUrl = urlBuilder.build().toString();
        return mUrl;
    }
    private String getRequestPhoneRestartUrl(String region, String projectId) {
        HttpUrl.Builder urlBuilder = HttpUrl.parse("https://cph." + region + ".myhuaweicloud.com/v1/"+ projectId + "/cloud-phone/phones/batch-restart").newBuilder();
        mUrl = urlBuilder.build().toString();
        return mUrl;
    }
    private String getRequestPhoneResetUrl(String region, String projectId) {
        HttpUrl.Builder urlBuilder = HttpUrl.parse("https://cph." + region + ".myhuaweicloud.com/v1/"+ projectId + "/cloud-phone/phones/batch-reset").newBuilder();
        mUrl = urlBuilder.build().toString();
        return mUrl;
    }

}
